/** @brief Define the transaction manager class.
 *
 *  transMgr.h
 *  kangmodb
 *
 *  Created by 강모 김 on 11. 4. 24..
 *  Copyright 2011 강모소프트. All rights reserved.
 *
 */

#ifndef _KD_TRANS_MGR_H_
#define _KD_TRANS_MGR_H_ (1)

#include "kdInfra.h"
#include "types.h"
#include "heap.h"
#include "transaction.h"

/** @brief The transaction pool where the transaction objects are allocated.
 */
class stgTransactionPool
{
public :
    /** @brief Allocate a new transaction from the pool.
     */
	KD_VOID checkout(stgTransaction ** tx) 
	{
		KD_TRY 
		{
 		    KD_ASSERT( tx != NULL );

			// TODO : Optimization : Need to implement transaction pool. For now, simply use "new" operator.
			*tx = new stgTransaction();
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
    /** @brief Return a transaction into the pool.
     */
	KD_VOID checkin(stgTransaction * tx)
	{
		KD_TRY 
		{
			KD_ASSERT( tx != NULL );

			// TODO : Optimization : Need to implement transaction pool. For now, simply use "delete" operator.
			delete tx;
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
};

class stgTransactionManager
{
private :
	/** @brief The maximum committed version. all versions up to maxCommitVersion_ are either committed or reverted.
	 *  In other words, there is no on-going COMMIT of any transaction up to maxCommitVersion_.
	 *  It means readers can read records with versions up to maxCommitVersion_;
	 */
	stgVersion maxCommitVersion_;
	
	/** @brief The maximum commit version that commiting transaction allocated. 
	 *  Versions within [ (maxCommitVersion_ + 1), newCommitVersion ] can have on-going COMMITs,
	 *  so readers should not read these versions. ( KangmoDB supports read-committed isolation level. )
	 */
	stgVersion newCommitVersion_;
	
	/** @brief This heap keeps active transactions in the order of the maximum version that the transaction can read(maxReadVersion).
	 *  The stale records up to the 2nd newest one up to the minimum version within this heap can be reclaimed.
	 *  The 1st newest should not be reclaimed, because active transactions can read it.
	 */
	kdHeap<stgVersion, stgTransaction*> activeTxHeap_;
	
	/** @brief This heap keeps transactions that are commiting in the order of the new commit version allocated to each commiting transaction 
	 *  Whenever a transaction successfully commits(commitVersion is called) or failes(revertVerson is called) to commit, it is removed from this heap.
	 *  After removal, update maxCommitVersion_ to MIN(version) in the heap if it is greater than maxCommitVersion_
	 *  so that reading transactions can read versions of the successfully committed one. 
	 *  ( Transactions that failed to commit will remove the new versions from in-memory data strucure before calling revertVersion, 
	 *    so no transactions can read them even though the reverted version is less than or equal to the maxCommitVersion_. )
	 */
	kdHeap<stgVersion, stgTransaction*> ongoingCommitTxHeap_;
	
public :
	stgTransactionManager()
	{
	}
	
	/** @brief Add an active transaction to the tranasction manager.
	 */
	KD_VOID addActive(stgTransaction * tx) 
	{
		KD_TRY 
		{
			// BUGBUG need a mutext on activeTxHeap_ 
			KD_ASSERT(0);
			
			KD_CALL( activeTxHeap_.add( tx->getViewVersion(), tx ) );
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Remove an active transaction from the tranasction manager.
	 */
	KD_VOID removeActive(stgTransaction * tx)
	{
		KD_TRY 
		{
			// BUGBUG need a mutext on activeTxHeap_ 
			KD_ASSERT(0);
			
			KD_CALL( activeTxHeap_.remove( tx->getViewVersion(), tx ) );
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Get the new version for a committing transaction 
	 */
	KD_VOID allocCommitVersion( stgTransaction * tx )
	{
		KD_TRY
		{
			// BUGBUG need a mutext on newCommitVersion_ and ongoingCommitTxHeap_
			KD_ASSERT(0);
			
			newCommitVersion_ = newCommitVersion_ + 1;
			tx->setCommittingVersion(newCommitVersion_);
			KD_CALL( ongoingCommitTxHeap_.add(newCommitVersion_, tx ) );
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Commit a new version.  
	 *  Set the maxCommitVersion with the new commit version.
	 *  This function is called after a transaction successfully committted.
	 *  Readers can read the version if there is no on-going commit between maxCommitVersion_ and tx->getCommittingVersion().
	 */
	KD_VOID commitVersion( stgTransaction * tx )
	{
		stgVersion minOngoingVersion;
		
		KD_TRY
		{
			// BUGBUG need a mutext on ongoingCommitTxHeap_
			KD_ASSERT(0);
			
			// Remove the tranasction from the on-going commit transactions. 
			KD_CALL( ongoingCommitTxHeap_.remove(tx->getCommittingVersion(), tx ) );
			
			// Update maxCommitVersion_ to reflect the commited transaction so that other transactions can see the modifications.
			KD_CALL( ongoingCommitTxHeap_.getMin( & minOngoingVersion) );
			
			// BUGBUG need a mutext on newCommitVersion_
			KD_ASSERT(0);
			 
			// The minOngoingVersion is used by on-going commit. 
			// Need to set the maxCommitVersion with a previous version of minOngoingVersion.
			if ( maxCommitVersion_ < (minOngoingVersion-1) )
			{
			    maxCommitVersion_ = (minOngoingVersion-1);
			}

			// The transaction is not committing anymore. Reset the committing version.
			tx->setCommittingVersion( STG_VERSION_NULL );
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Revert a new version. The version won't be used by any committing transaction.
	 *  This function is called when a transaction fails to commit 
	 *  after it allocated a new commit version by calling allocCommitVersion.
	 *  The new commit version won't be used by any transaction.
	 */
	KD_VOID revertVersion( stgTransaction * tx )
	{
		KD_TRY
		{
			// BUGBUG need a mutext on ongoingCommitTxHeap_
			KD_ASSERT(0);
			
			// Remove the tranasction from the on-going commit transactions. 
			KD_CALL( ongoingCommitTxHeap_.remove(tx->getCommittingVersion(), tx ) );
			
			// The transaction is not committing anymore. Reset the committing version.
			tx->setCommittingVersion( STG_VERSION_NULL );
			
			// No need to update newCommitVersion_, 
			// because no transaction can see the touched record versions by the reverted transaction
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	
	/** @breif Get the maximum committed version that readers can read. 
	 *
	 */
	KD_VOID getCommitVersion( stgVersion * commitVersion )
	{
		KD_TRY
		{
			*commitVersion = maxCommitVersion_;
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
};

#endif /* _KD_TRANS_MGR_H_ */
